using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class attractScript : MonoBehaviour {
    public int instant;
    public int positionInPartition;
    public string firstNoteAdded;

    private GameObject target;
    private Transform listenner;
    private Transform gravityZone;
    private Transform[] particles = new Transform[2];

    private Note note = null;
    public GameObject prevAttract = null;
    public float gravityRange = 20f;
    public bool clockwiseRotation = true;

    private float size;
    private float scaleModifier = 0.001f;
    private bool isGravityEnabled = false;
    public bool orbitalTrajectoryEnabled = false;
    private bool isPlayingSound = false;
    public bool alreadyVisited = false;

    // Use this for initialization
    void Start()
    {
        getGravityZone();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        if (isPlayingSound)
            stopSoundIfSoFar();

        if (isGravityEnabled || name == "attract")
        {
            playGravity();
            rotate();
        }

    }

    void Update()
    {
    }

    public Transform getGravityZone()
    {
        if (gravityZone == null)
            gravityZone = transform.FindChild("gravityZone");
        return gravityZone;
    }

    public Transform[] getParticles()
    {
        if (particles[0] == null || particles[1] == null)
        {
            particles[0] = transform.FindChild("particles1");
            particles[1] = transform.FindChild("particles2");
        }
        return particles;
    }

    public Note getNote()
    {
        return note;
    }

    public void transformWithNote(Note note, GameObject prevAttract)
    {
        this.note = note;
        this.prevAttract = prevAttract;
        firstNoteAdded = note.textNotation(true) + " (pitch=" + note.pitch + ")";

        //Name
        name = "attract_" + note.index;
        target = this.gameObject;
        target.name = name;

        //audio
        addNote(note);

        //Size
        size = noteLengthToSize(note.length);
        Vector3 size3 = new Vector3(size, size, size);
        transform.localScale = size3;
        gravityRange += size;

        //Position
        if (GeneralParameters.onPartition)
            transform.position = generateOnPartitionTranslationVector();
        else
            transform.position = generateRandomTranslationVector();

        //Mass
        rigidbody.mass = size * GeneralParameters.relativeMass;

        //Childs
        disable();


        positionInPartition = note.positionInPartition();

    }

    public void addNote(Note note)
    {
        if (GeneralParameters.debug) print("AddNote to " + target.name);
        AudioSource newAudioSource = target.AddComponent<AudioSource>();
        newAudioSource.clip = GeneralParameters.referenceAudioClip;
        newAudioSource.pitch = note.pitch;
        newAudioSource.loop = true;
        newAudioSource.dopplerLevel = 0;
        newAudioSource.rolloffMode = AudioRolloffMode.Linear;
        newAudioSource.minDistance = 100;
        newAudioSource.maxDistance = 100;
        newAudioSource.playOnAwake = false;
        newAudioSource.Stop();
        //newAudioSource.volume = 0.4f; //TODO
    }

    private Vector3 generateRandomTranslationVector()
    {
        float distance = 0;
        if (instant > 0)
        {
            Note prevNote = prevAttract.GetComponent<attractScript>().getNote();
            distance = noteLengthToDistance(getNote().startTime - prevNote.startTime);
        }
        float angle = Random.Range(GeneralParameters.minAngle, GeneralParameters.maxAngle);
        float x = distance * Mathf.Cos(Mathf.Deg2Rad * angle);
        float z = distance * Mathf.Sin(Mathf.Deg2Rad * angle);

        float prevX = prevAttract.transform.position.x;
        float prevZ = prevAttract.transform.position.z;

        x += prevX;
        z += prevZ;
        return new Vector3(x, 0, z);
    }

    private Vector3 generateOnPartitionTranslationVector()
    {
        float z = getNote().positionInPartition() * GeneralParameters.semitoneHeight;
        float x = noteLengthToDistance(getNote().startTime);
        return new Vector3(x, 0, z);
    }

    /**
     * @source : http://www.unifycommunity.com/wiki/index.php?title=Gravity
     * */
    private void playGravity()
    {
        rigidbody.velocity = Vector3.zero;
        Collider[] colliders = Physics.OverlapSphere(transform.position, gravityRange);
        List<Rigidbody> rbodies = new List<Rigidbody>();

        foreach (Collider collider in colliders)
        {
            Rigidbody rbody = collider.attachedRigidbody;
            if (rbody != null && rbody != rigidbody && rbody.gameObject.name == "ship")
            {

                if (rbodies.Contains(rbody))
                {
                    continue;
                }
                if (GeneralParameters.debug) print("Attraction");
                rbodies.Add(rbody);
                Vector3 offset = transform.position - collider.transform.position;
                rbody.AddForce(offset / offset.sqrMagnitude * rigidbody.mass);
            }
        }

        animateGravityZone();
    }

    private void rotate(){
        //float speed = size * Time.smoothDeltaTime * 10;
        float attractSpeed = GeneralParameters.relativeAngularVelocity * Time.smoothDeltaTime;
        if (name == "attract")
            attractSpeed = 10 * Time.smoothDeltaTime;
        if (clockwiseRotation)
        {
            rigidbody.angularVelocity = new Vector3(0, attractSpeed, 0);
            //gravityZone.Rotate(new Vector3(0, -1 * speed, 0));
        }
        else
        {
            rigidbody.angularVelocity = new Vector3(0, -1 * attractSpeed, 0);
            //gravityZone.Rotate(new Vector3(0, speed, 0));
        }
    }

    private void animateGravityZone()
    {
        //texture
        Vector2 textureScale = gravityZone.renderer.material.mainTextureScale;
        if (textureScale.x > 2 || textureScale.x < 0)
            scaleModifier *= -1;
        textureScale.x += scaleModifier;
        textureScale.y -= scaleModifier;
        gravityZone.renderer.material.mainTextureScale = textureScale;

        //size
        /*float maxVariationRatio = 1.5f;
        float deltaX = 0.01f;
        float deltaZ = 0.02f;
        float originSize = 2;
        if (gravityZone.localScale.x > originSize + maxVariationRatio || gravityZone.localScale.x < originSize - maxVariationRatio)
            deltaX = -0.01f;
        if (gravityZone.localScale.z > originSize + maxVariationRatio || gravityZone.localScale.z < originSize - maxVariationRatio)
            deltaZ = -0.02f;
        gravityZone.localScale = new Vector3(gravityZone.localScale.x + deltaX, originSize, gravityZone.localScale.z + deltaZ);*/

    }

    public void enable()
    {
        isGravityEnabled = true;
        getGravityZone().gameObject.active = true;
        /*foreach (Transform p in getParticles())
            p.gameObject.active = true;*/
    }

    public void disable()
    {
        isGravityEnabled = false;
        getGravityZone().gameObject.active = false;
        foreach (Transform p in getParticles())
            p.gameObject.active = false;

    }

    public void playSound(Transform listenerPosition)
    {
        listenner = listenerPosition;
        AudioSource[] audioSources = GetComponentsInChildren<AudioSource>();
        foreach (AudioSource audioSource in audioSources)
        {
            audioSource.Play();
        }
        isPlayingSound = true;
    }

    public void stopSound()
    {
        if (isPlayingSound)
        {
            AudioSource[] audioSources = GetComponentsInChildren<AudioSource>();
            foreach (AudioSource audioSource in audioSources)
            {
                audioSource.Stop();
            }
            isPlayingSound = false;
        }
    }

    private void stopSoundIfSoFar()
    {
        float sqrOffset = (transform.position - listenner.position).sqrMagnitude;
        if (sqrOffset > Mathf.Pow(noteLengthToDistance(getNote().length), 2))
        {
            stopSound();
        }
    }

    private float noteLengthToDistance(float length)
    {
        return length * GeneralParameters.quarterNoteDistance * 120 / GeneralParameters.defaultTempo;
    }

    private float noteLengthToSize(float length)
    {
        if (length > 2) length = 2; //max size
        return length * GeneralParameters.quarterNoteSize * 120 / GeneralParameters.defaultTempo;
    }

    public override string ToString()
    {
        string ret = name + " : " + getNote();
        return ret;
    }

}